-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local Class = require 'mc.class'
local skynet = require 'skynet'
local custom_tool = require 'common_tools.custom_tool'
local c_chassis_service = require 'chassis.service'
local led_i2c_service = require 'led_i2c_service'
local chassis_service = require 'chassis_service'
local led_service = require 'led_service'
local led_policy_service = require 'led_policy_service'
local ncsi_capabilities_service = require 'ncsi_capabilities_service'
local led_tube_service = require 'led_tube_service'
local log = require 'mc.logging'
local object_manage = require 'mc.mdb.object_manage'
local reboot_manage = require 'mc.mdb.micro_component.reboot'
local debug = require 'mc.mdb.micro_component.debug'
local ipmi_req = require 'chassis.ipmi.ipmi'
local metric_collect = require 'metric_collect'
local mc_admin = require 'mc.mc_admin'
local utils_core = require 'utils.core'
local tasks = require "mc.orm.tasks"
local mdb = require 'mc.mdb'
local factory = require 'factory'
local client = require 'chassis.client'
local c_object_manage = require 'mc.orm.object_manage'
local super_pod_label_collection = require 'super_pod_label_collection'
local enclosure_service = require 'enclosure_service'
local config_server = require 'config_mgmt.server'

local chassis_app = Class(c_chassis_service)
function chassis_app:start_system_health_listenning()
    self.health_monitor_task = tasks.get_instance():new_task("health monitor task"):loop(function ()
        local _, obj = pcall(mdb.get_object, self.bus, '/bmc/kepler/Systems/1/Events', 'bmc.kepler.Systems.Events')
        if not obj or not obj.Health then
            return
        end
        log:debug("set (%s) health led for the first time", obj.Health)
        self.led_service:set_led_by_sys_health(obj.Health)
        self.health_monitor_task:stop()
        self.health_monitor_task = nil
    end):set_timeout_ms(2000)
    client:OnEventsPropertiesChanged(function(values, msgpath, _)
        if values['Health'] then
            local health = values['Health']:value()
            log:notice('Chassis_app get_event_health change to:%s', health)
            self.led_service:set_led_by_sys_health(health)
        end
    end, {SystemId = 1})
end

function chassis_app:start_sel_health_listenning()
    self.health_monitor_task = tasks.get_instance():new_task("sel health monitor task"):loop(function ()
        local _, obj = pcall(mdb.get_object, self.bus, '/bmc/kepler/Chassis/1/Sensors', 'bmc.kepler.Chassis.Sensors')
        if not obj or not obj.Health then
            return
        end
        log:debug("set (%s) sel health led for the first time", obj.Health)
        self.led_service:set_led_by_sys_health(obj.Health)
        self.health_monitor_task:stop()
        self.health_monitor_task = nil
    end):set_timeout_ms(2000)
    client:OnSensorsPropertiesChanged(function(values, msgpath, _)
        if values['Health'] then
            local health = values['Health']:value()
            log:notice('Chassis_app get_sel_health change to:%s', health)
            self.led_service:set_led_by_sys_health(health)
        end
    end, {SystemId = 1})
end

-- 自发现分发对象
function chassis_app:on_add_object(class_name, object, position)
    local switch = {
        ['LedPolicy'] = function()
            self.led_policy_service:add_object(object)
        end,
        ['Led'] = function()
            self.led_position = position
            self.led_service:add_object(object)
        end,
        ['Chassis'] = function()
            self.chassis_position = position
            self.chassis_service:add_object(object, position)
        end,
        ['NCSICapabilities'] = function()
            self.ncsi_capabilities_service:add_object(object)
        end,
        ['LedDispControl'] = function()
            self.led_tube_service:add_object(object, position)
        end,
        ['I2cLed'] = function()
            self.led_i2c_service:add_object(object, position)
        end
    }
    if switch[class_name] then
        switch[class_name]()
        log:info('[chassis_app] Add object, Class: %s', class_name)
    end
end

function chassis_app:ctor()
    self.led_policy_service = led_policy_service.new()
    self.led_service = led_service.new(self.bus)
    self.chassis_service = chassis_service.new(self.bus, self.led_service, self.db)
    self.ncsi_capabilities_service = ncsi_capabilities_service.new()
    self.led_tube_service = led_tube_service.new(self.bus)
    self.led_i2c_service = led_i2c_service.new()
    self.super_pod_label_service = super_pod_label_collection.get_instance()
    self.metric_collect = metric_collect.new(self.bus)
    self.dft_obj_list = {}
    factory:register('chassis_service', self.chassis_service)
    factory:register('led_service', self.led_service)
    enclosure_service.new(self.bus)
end

function chassis_app:check_dependencies()
    local admin = mc_admin.new()
    admin:parse_dependency(utils_core.getcwd() .. '/mds/service.json')
    admin:check_dependency(self.bus)
end

function chassis_app:register_ipmi()
    self:register_ipmi_cmd(ipmi_req.GetDeviceCapabilities, function(...)
        return self.ncsi_capabilities_service:GetDeviceCapabilities(...)
    end)
    self:register_ipmi_cmd(ipmi_req.SetDeviceCapabilities, function(...)
        return self.ncsi_capabilities_service:SetDeviceCapabilities(...)
    end)
    self:register_ipmi_cmd(ipmi_req.ChassisIdentify, function(req, ctx)
        return self.led_service:ipmi_chassis_identify(req, ctx)
    end)
    self:register_ipmi_cmd(ipmi_req.SetFruLedState, function(...)
        return self.led_service:ipmi_set_fru_led_state(...)
    end)
    self:register_ipmi_cmd(ipmi_req.GetPowerCmd, function(...)
        return self.chassis_service:ipmi_get_power_status(...)
    end)
    self:register_ipmi_cmd(ipmi_req.GetChassisHeight, function(req, ctx)
        return self.chassis_service:ipmi_get_chassis_height(req, ctx)
    end)
    self:register_ipmi_cmd(ipmi_req.GetLedColorCapabilities, function(req, ctx)
        return self.led_service:ipmi_get_led_color_capabilities(req, ctx)
    end)
    self:register_ipmi_cmd(ipmi_req.GetShelfInfo, function(req, ctx)
        return enclosure_service.get_instance():ipmi_get_shelf_info(req, ctx)
    end)
    self:register_ipmi_cmd(ipmi_req.GetSuperPodLabelInfo, function(req, ctx)
        return self.super_pod_label_service:ipmi_get_superpod_label_info(req, ctx)
    end)
    self:register_ipmi_cmd(ipmi_req.SetSuperPodLabelInfo, function(req, ctx)
        return self.super_pod_label_service:ipmi_set_superpod_label_info(req, ctx)
    end)
    self:register_ipmi_cmd(ipmi_req.SetSuperPodId, function(req, ctx)
        return self.super_pod_label_service:ipmi_set_super_pod_id(req, ctx)
    end)
    self:register_ipmi_cmd(ipmi_req.SetServerIndex, function(req, ctx)
        return self.super_pod_label_service:ipmi_set_server_index(req, ctx)
    end)
    self:register_ipmi_cmd(ipmi_req.SetSuperPodSize, function(req, ctx)
        return self.super_pod_label_service:ipmi_set_super_pod_size(req, ctx)
    end)
end

function chassis_app:on_dump(ctx, path)
    self.led_service:led_dump(ctx, path)
end

function chassis_app:init()
    log:notice("start super init")
    chassis_app.super.init(self)
    log:notice("start check init")
    self:check_dependencies()
    log:notice("end check init")
    self.led_service.sys_obj_ok:on(function ()
        local customer = custom_tool.get_customer()
        log:notice('Chassis_app get_customer:%s', customer)
        if customer ~= 'ByteDance' then
            self:start_system_health_listenning()
        else
            self:start_sel_health_listenning()
        end
    end)
    log:notice('sys_obj_ok_after')
    skynet.fork_once(function ()
        log:notice("start fork init")
        self:listen_hwdiscovery()
        self:register_rpc_methods()
        self:register_reboot_methods()
        self:register_ipmi()
        debug.on_dump(function (...)
            self:on_dump(...)
        end)
        log:notice("end fork init")
    end)
    -- 配置导入导出注册
    config_server.new(self.db, self.bus, self.datas)
    log:notice("enclosure task start")
    enclosure_service.get_instance():update_data_from_smm()
end

function chassis_app:listen_hwdiscovery()
    -- 注册对象响应回调函数
    -- 添加对象回调
    -- c_object_manage的new, start函数必须和on_add_object写在一起,保证没有协程切换
    self.object_manage = c_object_manage.new(self.db, self.bus)
    self.object_manage:start()
    object_manage.on_add_object(self.bus, function(class_name, object, position)
        self:on_add_object(class_name, object, position)
        self:on_add_dft_object(class_name, object, position)
    end)
    -- 删除对象回调
    object_manage.on_delete_object(self.bus, function(class_name, object, position)
    end)
    -- 添加对象完成回调
    object_manage.on_add_object_complete(self.bus, function(position)
        self.chassis_service:on_add_object_complete(position)
        self.led_tube_service:add_object_complete(position)
        if position == self.led_position then
            skynet.fork_once(function ()
                self.led_service:bmc_reset_uid_flash(self.db)
                self.led_service:update_led_info()
            end)
        end
        if position == self.chassis_position then
            self:init_update_intrusion_status()
            self.chassis_service:uid_button_monitor()
            self.chassis_service:long_press_uid_check_task()
        end
    end)
    -- 删除对象完成回调
    object_manage.on_delete_object_complete(self.bus, function(position)
        self.chassis_service:on_delete_object(position)
    end)
end

--注册开箱事件自检任务
function chassis_app:init_update_intrusion_status()
    -- 等待1min，确保Event_ChassisCoverOpen对象注册完毕
    skynet.sleep(6000)
    skynet.fork_loop({count = 0}, function()
        while true do
            -- 硬件扫描周期5s,软件扫描周期6s,保证数据变更后再读取
            skynet.sleep(600)
            self.chassis_service:update_intrusion_status()
        end
    end)
end

function chassis_app:register_rpc_methods()
    self:ImplLedLedSetState(function(obj, ctx, ...)
        return self:SetUIDLedIdentify(obj, ctx, ...)
    end)
    self:ImplChassisMetricGetItems(function(obj, ctx, ...)
        return self.metric_collect:get_chassis_data_collection_items(obj, ...)
    end)
    self:ImplChassisMetricGetData(function(obj, ctx, ...)
        return self.metric_collect:get_chassis_data_collection_data(obj, ...)
    end)
    self:ImplSuperPodLabelSuperPodLabelSetSuperPodId(function(obj, ctx, ...)
        return self.super_pod_label_service:SetSuperPodId(ctx, ...)
    end)
    self:ImplSuperPodLabelSuperPodLabelSetServerIndex(function(obj, ctx, ...)
        return self.super_pod_label_service:SetServerIndex(ctx, ...)
    end)
    self:ImplSuperPodLabelSuperPodLabelSetSuperPodSize(function(obj, ctx, ...)
        return self.super_pod_label_service:SetSuperPodSize(ctx, ...)
    end)
    self:ImplSuperPodLabelSuperPodLabelRestoreDefaults(function(obj, ctx, ...)
        return self.super_pod_label_service:RestoreDefaults(ctx)
    end)
end

function chassis_app:on_add_dft_object(class_name, object, position)
    local switch = {
        ['DftUidButton'] = 1,
        ['DftChassisCover'] = 1,
        ['DftPysicalLed'] = 1,
        ['DftLedIntelligence'] = 1,
        ['DftLedTube'] = 1,
        ['DftLedTubeIntelligence'] = 1,
        ['DftButtonTest'] = 1
    }

    if switch[class_name] then
        self.dft_obj_list[object.name] = object
    end
end

-- 调用RPC方法，设置led灯的状态
function chassis_app:SetUIDLedIdentify(obj, ctx, State, Interval)
    return self.led_service:set_uid_led_identify(obj, ctx, State, Interval)
end

function chassis_app:on_reboot_prepare()
    log:info('[chassis_app] reboot prepare')
end

function chassis_app:on_reboot_cancel()
    log:info('[chassis_app] reboot cancel')
end

function chassis_app:on_reboot_action()
    log:info('[chassis_app] reboot action')
end

function chassis_app:register_reboot_methods()
    -- 注册平滑重启回调函数
    -- Prepare准备重启回调
    reboot_manage.on_prepare(function()
        self:on_reboot_prepare()
        return 0
    end)
    -- Action执行重启回调
    reboot_manage.on_action(function()
        self:on_reboot_action()
        return 0
    end)
    -- Cancel取消重启回调
    reboot_manage.on_cancel(function()
        self:on_reboot_cancel()
    end)
end

return chassis_app
